/*
** ---> This Test State is testing the following behaviours
** ---> Seek - Face - Flee - LookWhereYoureGoing - Wander
**
** ---> Louie
*/

#include "TestState3.h"
#include "Graphics.h"
#include "Timer.h"
#include "MyGL.h"
#include "Game.h"
#include "Error.h"
#include "Mesh.h"
#include "AssetManager.h"
#include "StringUtility.h"
#include "Texture.h"
#include "SteeringOutput.h"
#include "World.h"
#include "Pathfinding.h"
#include "BulletPhysics.h"

#include <math.h>
#include <vector>
#include <SDL_events.h>
#include <iostream>
#include <windows.h>

TestState3::TestState3()
{
	m_hash		= Engine::Hash(GetName());
	m_target	= 0;

	isclick = false;
}

bool ts3 = Engine::TheGame::Instance() ->RegisterGameState(TheTestState3::Instance());

void TestState3::OnMouseButtonEvent(const SDL_MouseButtonEvent& m)
{
	switch(m.type)
	{
	case SDL_MOUSEBUTTONDOWN:
		if(m.button == SDL_BUTTON_LEFT)
		{
			printf("m x: %d \n", m.x);
			printf("m y: %d \n", m.y);

			MouseToWorldCoords(m.x, m.y);

			isclick = true;
		}
		break;

	case SDL_MOUSEBUTTONUP:

		break;
	}
}

void TestState3::OnKeyboardEvent(const SDL_KeyboardEvent& k)
{
	if(k.keysym.sym == SDLK_ESCAPE) 
	{
		Engine::TheGame::Instance() ->SetCurrentGameState(nullptr);
	}
	
	float s = 8;

	GameCamera* c = TheGameCamera::Instance();
	
	switch(k.type)
	{
		case SDL_KEYDOWN:
			if(k.keysym.sym == SDLK_w)
				c ->SetVel(c ->GetVel() + Engine::Vec3(0,0,-s));
			if(k.keysym.sym == SDLK_s)
				c ->SetVel(c ->GetVel() + Engine::Vec3(0,0,s));
			if(k.keysym.sym == SDLK_a)
				c ->SetVel(c ->GetVel() + Engine::Vec3(-s,0,0));
			if(k.keysym.sym == SDLK_d)
				c ->SetVel(c ->GetVel() + Engine::Vec3(s,0,0));
			if(k.keysym.sym == SDLK_o)
				c ->SetVel(c ->GetVel() + Engine::Vec3(0,s,0));
			if(k.keysym.sym == SDLK_l)
				c ->SetVel(c ->GetVel() + Engine::Vec3(0,-s,0));

			break;

		case SDL_KEYUP:
			if(k.keysym.sym == SDLK_w)
				c ->SetVel(c ->GetVel() - Engine::Vec3(0,0,-s));
			if(k.keysym.sym == SDLK_s)
				c ->SetVel(c ->GetVel() - Engine::Vec3(0,0,s));
			if(k.keysym.sym == SDLK_a)
				c ->SetVel(c ->GetVel() - Engine::Vec3(-s,0,0));
			if(k.keysym.sym == SDLK_d)
				c ->SetVel(c ->GetVel() - Engine::Vec3(s,0,0));
			if(k.keysym.sym == SDLK_o)
				c ->SetVel(c ->GetVel() - Engine::Vec3(0,s,0));
			if(k.keysym.sym == SDLK_l)
				c ->SetVel(c ->GetVel() - Engine::Vec3(0,-s,0));
	}

	//case SDL_KEYUP:
	//	TheGameCamera::Instance()
	//			->SetVel(Engine::Vec3(0,0,0));
	//	break;
	//}
}

void TestState3::OnQuit()
{
	Engine::TheGame::Instance() ->SetCurrentGameState(nullptr);
};

void TestState3::Activate()
{
	GameState::Activate();

	// ---> Set up camera first
	//
	TheGameCamera::Instance()->SetPos(Engine::Vec3(0, 30, 20));
	TheGameCamera::Instance()->SetFoc(Engine::Vec3(0, -1, 0));

	sb_fap = new Engine::FollowPath;
	
	sb_fap->SetDistOffset(3.0f);

	sb_lwyg		= new Engine::LookWhereYoureGoing;
	sb_lwyg		->SetSlowRadius(0.1f);
	sb_lwyg		->SetTimeToTarget(0.3f);
	sb_lwyg		->SetTargetRadius(0.05f);

	m_target	= new Engine::Unit;
	m_target	->SetPos(Engine::Vec3(-10.0f, 0.0f, 0.0f));
	m_target	->SetScale(Engine::Vec3(1.0f, 0.5f, 1.0f));
	m_target	->SetMaxSpeed(35.0f);
	m_target	->SetMaxAngularVel(6.0f);
	m_target	->SetMaxAngularAcc(10.0f);

	m_target    ->Initialize();

	
	TheWorld::Instance() ->CreateRandom(10, 10, 2.0f, 2.0f, 0);

	Engine::TheBtPhysics::Instance()->Initialize();

	////
	//// ---> Pathfinding testing here
	////
	//Engine::Vec2 sc = Engine::Vec2(17, 17); //---> grid pos
	//Engine::Vec2 ec = Engine::Vec2(18, 0); // ---> grid pos

	//bool pathFound = Engine::ThePathfinding::Instance()->FindPath(sc, ec);
	//
	//if(pathFound)
	//{
	//	int pathSize = Engine::ThePathfinding::Instance()->GetPathSize();

	//	for(int i = 0; i < pathSize; i++)
	//	{
	//		Engine::Vec2 next = Engine::ThePathfinding::Instance()->NextPathPos();
	//		p.PopulateVec(next);
	//	}
	//}
	//else
	//{
	//	printf("ERROR: path not found . . . \n");
	//	p.PopulateVec(sc);
	//	p.PopulateVec(ec);
	//}
}

void TestState3::Update()
{
	TheGameCamera::Instance()->Update();
	
	//m_target	->SetPos(mouseToWorld);

	Engine::TheBtPhysics::Instance()->Update(rnear, rfar);

	if(isclick)
	{
		UpdatePath();
		sb_fap->SetPath(&p);
		m_target	->AddSteeringBehaviour(Engine::WeightedSteeringBehaviour(sb_fap, 1.0f), m_target);
		m_target	->AddSteeringBehaviour(Engine::WeightedSteeringBehaviour(sb_lwyg, 0.1f), m_target); 
		isclick = false;
	}

	Engine::TheGame::Instance()->UpdateGameObjects();
}

void TestState3::Draw()
{
	//if(isclick)
	//{
	p.DrawPath();
	//}

	TheWorld::Instance() ->Draw();

	TheGameCamera::Instance()->Draw();
	
	Engine::TheBtPhysics::Instance()->Draw();

	Engine::TheGame::Instance()->DrawGameObjects();
}

void TestState3::Deactivate()
{
	Engine::TheBtPhysics::Instance()->Shutdown();
}

void TestState3::UpdatePath()
{
	mouseToWorld = Engine::TheBtPhysics::Instance()->GetP();

	//printf("mtwx: %f \n", mouseToWorld.x);
	//printf("mtwy: %f \n", mouseToWorld.y);
	//printf("mtwz: %f \n", mouseToWorld.z);

	Engine::Vec2 s = Engine::Vec2(m_target->GetPos().x, m_target->GetPos().z);
	Engine::Vec2 e = Engine::Vec2(mouseToWorld.x, mouseToWorld.z);

	bool pathFound = Engine::ThePathfinding::Instance()->AStar(s, e);
	
	if(pathFound)
	{
		int pathSize = Engine::ThePathfinding::Instance()->GetPathSize();

		printf("path size: %d \n", pathSize);

		for(int i = 0; i < pathSize; i++)
		{
			Engine::Vec2 next = Engine::ThePathfinding::Instance()->NextPathPos();
			p.PopulateVec(next);
		}
	}
	else
	{
		printf("ERROR: path not found . . . \n");
		p.PopulateVec(s);
		p.PopulateVec(e);
	}
}

Engine::Vec3 TestState3::MouseToWorldCoords(int x, int y)
{
	GLint viewport[4];
	GLdouble modelview[16];
	GLdouble projection[16];
	GLfloat winX, winY;
	GLdouble nearX, nearY, nearZ;
	GLdouble farX, farY, farZ;

	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	glGetIntegerv(GL_VIEWPORT, viewport);

	winX = (float)x;
	winY = (float)viewport[3] - (float)y;

	gluUnProject(winX, winY, 0.0, modelview, projection, viewport, &nearX, &nearY, &nearZ);
	rnear = Engine::Vec3(nearX, nearY, nearZ);

	gluUnProject(winX, winY, 1.0, modelview, projection, viewport, &farX, &farY, &farZ);
	rfar = Engine::Vec3(farX, farY, farZ);

	Engine::Vec3 ray = rnear - rfar;
	float raylength = ray.Length();

	ray.Normalize();

	return ray;
}